[PATCH] output/jsonbuilder: helper function SCJbSetPrintAsciiString
authorPhilippe Antoine <pantoine@oisf.net>
Thu, 30 Oct 2025 10:18:15 +0000 (11:18 +0100)
committerAndreas Dolp <dev@andreas-dolp.de>
Wed, 10 Dec 2025 19:12:20 +0000 (20:12 +0100)
To replace C PrintStringsToBuffer and avoid a stack alloc
+ copy

Ticket: 8004
(cherry picked from commit 7447651fa0956ff4ce55283a51b4a9494ec8cc6a)

Origin: upstream, https://github.com/OISF/suricata/commit/5abf9b81e78476f49ab074f3a74b5840747cd069.patch
Bug: https://redmine.openinfosecfoundation.org/issues/8004
Subject: Upstream fix for CVE-2025-64331

Gbp-Pq: Name CVE-2025-64331.patch

rust/dist/rust-bindings.h
rust/src/jsonbuilder.rs
src/output-json-alert.c
src/output-json-frame.c
src/output-json-http.c
src/output-json.c

index 6f3cd54e50dcec475144780906ed6e690b7992dd..47cd9ae9dd2ac813fc6cb46b7b9e8884ca65c560 100644 (file)
@@ -4898,6 +4898,11 @@ bool jb_set_string_from_bytes(struct JsonBuilder *js,
                               const uint8_t *bytes,
                               uint32_t len);
 
+bool SCJbSetPrintAsciiString(struct JsonBuilder *js,
+                             const char *key,
+                             const uint8_t *bytes,
+                             uint32_t len);
+
 bool jb_set_base64(struct JsonBuilder *js,
                    const char *key,
                    const uint8_t *bytes,
index 227ee40fdaaab788e6d9e5ed7285bef7b5738343..5418275baa8d4dcbc5e50ca0da659fac103e396b 100644 (file)
@@ -527,6 +527,52 @@ impl JsonBuilder {
         }
     }
 
+    /// Set a key with a string value taking only ascii-printable bytes.
+    /// Non-printable characters are replaced by a dot `.`, except
+    /// CR and LF which are escaped the regular json way \r and \n
+    pub fn set_print_ascii(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> {
+        match self.current_state() {
+            State::ObjectNth => {
+                self.push(',')?;
+            }
+            State::ObjectFirst => {
+                self.set_state(State::ObjectNth);
+            }
+            _ => {
+                debug_validate_fail!("invalid state");
+                return Err(JsonError::InvalidState);
+            }
+        }
+        self.push('"')?;
+        self.push_str(key)?;
+        self.push_str("\":\"")?;
+        for &x in val.iter() {
+            match x {
+                b'\r' => {
+                    self.push_str("\\r")?;
+                }
+                b'\n'=> {
+                    self.push_str("\\n")?;
+                }
+                b'"'=> {
+                    self.push_str("\\\"")?;
+                }
+                b'\\'=> {
+                    self.push_str("\\\\")?;
+                }
+                _ => {
+                    if !x.is_ascii() || x.is_ascii_control()  {
+                        self.push('.')?;
+                    } else {
+                        self.push(x as char)?;
+                    }
+                }
+            }
+        }
+        self.push('"')?;
+        Ok(self)
+    }
+
     /// Set a key and a string value (from bytes) on an object, with a limited size
     pub fn set_string_from_bytes_limited(&mut self, key: &str, val: &[u8], limit: usize) -> Result<&mut Self, JsonError> {
         let mut valtrunc = Vec::new();
@@ -884,6 +930,20 @@ pub unsafe extern "C" fn jb_set_string_from_bytes(
     return false;
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn SCJbSetPrintAsciiString(
+    js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32,
+) -> bool {
+    if bytes.is_null() || len == 0 {
+        return false;
+    }
+    if let Ok(key) = CStr::from_ptr(key).to_str() {
+        let val = std::slice::from_raw_parts(bytes, len as usize);
+        return js.set_print_ascii(key, val).is_ok();
+    }
+    return false;
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn jb_set_base64(
     js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32,
index 72127e5d0f14a17be2f320fc99213da13ae9e1e0..495b8285ff6b76be869bd3ea21350c53983add0a 100644 (file)
@@ -452,13 +452,7 @@ static void AlertAddPayload(AlertJsonOutputCtx *json_output_ctx, JsonBuilder *js
     }
 
     if (json_output_ctx->flags & LOG_JSON_PAYLOAD) {
-        uint8_t printable_buf[p->payload_len + 1];
-        uint32_t offset = 0;
-        PrintStringsToBuffer(printable_buf, &offset,
-                p->payload_len + 1,
-                p->payload, p->payload_len);
-        printable_buf[p->payload_len] = '\0';
-        jb_set_string(js, "payload_printable", (char *)printable_buf);
+        SCJbSetPrintAsciiString(js, "payload_printable", p->payload, p->payload_len);
     }
 }
 
@@ -764,11 +758,8 @@ static bool AlertJsonStreamData(const AlertJsonOutputCtx *json_output_ctx, JsonA
         }
 
         if (json_output_ctx->flags & LOG_JSON_PAYLOAD) {
-            uint8_t printable_buf[cbd.payload->offset + 1];
-            uint32_t offset = 0;
-            PrintStringsToBuffer(printable_buf, &offset, sizeof(printable_buf), cbd.payload->buffer,
-                    cbd.payload->offset);
-            jb_set_string(jb, "payload_printable", (char *)printable_buf);
+            SCJbSetPrintAsciiString(
+                    jb, "payload_printable", cbd.payload->buffer, cbd.payload->offset);
         }
         return true;
     }
index 69e74a7e92dd2297a49fb62ab6d64da8f076a5cc..8d53f30e53e16c3b55f2ce2d27dce722d02af397 100644 (file)
@@ -202,11 +202,7 @@ static void FrameAddPayloadTCP(Flow *f, const TcpSession *ssn, const TcpStream *
 
     if (cbd.payload->offset) {
         jb_set_base64(jb, "payload", cbd.payload->buffer, cbd.payload->offset);
-        uint8_t printable_buf[cbd.payload->offset + 1];
-        uint32_t offset = 0;
-        PrintStringsToBuffer(printable_buf, &offset, sizeof(printable_buf), cbd.payload->buffer,
-                cbd.payload->offset);
-        jb_set_string(jb, "payload_printable", (char *)printable_buf);
+        SCJbSetPrintAsciiString(jb, "payload_printable", cbd.payload->buffer, cbd.payload->offset);
         jb_set_bool(jb, "complete", complete);
     }
 }
@@ -235,11 +231,7 @@ static void FrameAddPayloadUDP(JsonBuilder *js, const Packet *p, const Frame *fr
     const uint32_t log_data_len = MIN(data_len, 256);
     jb_set_base64(js, "payload", data, log_data_len);
 
-    uint8_t printable_buf[log_data_len + 1];
-    uint32_t o = 0;
-    PrintStringsToBuffer(printable_buf, &o, log_data_len + 1, data, log_data_len);
-    printable_buf[log_data_len] = '\0';
-    jb_set_string(js, "payload_printable", (char *)printable_buf);
+    SCJbSetPrintAsciiString(js, "payload_printable", data, log_data_len);
 #if 0
     char pretty_buf[data_len * 4 + 1];
     pretty_buf[0] = '\0';
index c58c32fd017a51137f431c80068cd79f810775c4..87191caa9cebf034f5dffd026b0e72eb9345e461 100644 (file)
@@ -366,7 +366,6 @@ static void EveHttpLogJSONHeaders(
 static void BodyPrintableBuffer(JsonBuilder *js, HtpBody *body, const char *key)
 {
     if (body->sb != NULL && body->sb->region.buf != NULL) {
-        uint32_t offset = 0;
         const uint8_t *body_data;
         uint32_t body_data_len;
         uint64_t body_offset;
@@ -376,13 +375,7 @@ static void BodyPrintableBuffer(JsonBuilder *js, HtpBody *body, const char *key)
             return;
         }
 
-        uint8_t printable_buf[body_data_len + 1];
-        PrintStringsToBuffer(printable_buf, &offset,
-                             sizeof(printable_buf),
-                             body_data, body_data_len);
-        if (offset > 0) {
-            jb_set_string(js, key, (char *)printable_buf);
-        }
+        SCJbSetPrintAsciiString(js, key, body_data, body_data_len);
     }
 }
 
index e25ee516fbe6812c9c94132415334da35391b8af..6f80484012e15e30a8410099c8b040575fa9f28b 100644 (file)
@@ -210,22 +210,10 @@ static void EveAddPacketVars(const Packet *p, JsonBuilder *js_vars)
                 PrintStringsToBuffer(keybuf, &offset,
                         sizeof(keybuf),
                         pv->key, pv->key_len);
-                uint32_t len = pv->value_len;
-                uint8_t printable_buf[len + 1];
-                offset = 0;
-                PrintStringsToBuffer(printable_buf, &offset,
-                        sizeof(printable_buf),
-                        pv->value, pv->value_len);
-                jb_set_string(js_vars, (char *)keybuf, (char *)printable_buf);
+                SCJbSetPrintAsciiString(js_vars, (char *)keybuf, pv->value, pv->value_len);
             } else {
                 const char *varname = VarNameStoreLookupById(pv->id, VAR_TYPE_PKT_VAR);
-                uint32_t len = pv->value_len;
-                uint8_t printable_buf[len + 1];
-                uint32_t offset = 0;
-                PrintStringsToBuffer(printable_buf, &offset,
-                        sizeof(printable_buf),
-                        pv->value, pv->value_len);
-                jb_set_string(js_vars, varname, (char *)printable_buf);
+                SCJbSetPrintAsciiString(js_vars, varname, pv->value, pv->value_len);
             }
             jb_close(js_vars);
         }
@@ -276,15 +264,9 @@ static void EveAddFlowVars(const Flow *f, JsonBuilder *js_root, JsonBuilder **js
                             break;
                     }
 
-                    uint32_t len = fv->data.fv_str.value_len;
-                    uint8_t printable_buf[len + 1];
-                    uint32_t offset = 0;
-                    PrintStringsToBuffer(printable_buf, &offset,
-                            sizeof(printable_buf),
-                            fv->data.fv_str.value, fv->data.fv_str.value_len);
-
                     jb_start_object(js_flowvars);
-                    jb_set_string(js_flowvars, varname, (char *)printable_buf);
+                    SCJbSetPrintAsciiString(
+                            js_flowvars, varname, fv->data.fv_str.value, fv->data.fv_str.value_len);
                     jb_close(js_flowvars);
                 }
             } else if (fv->datatype == FLOWVAR_TYPE_STR && fv->key != NULL) {
@@ -300,15 +282,9 @@ static void EveAddFlowVars(const Flow *f, JsonBuilder *js_root, JsonBuilder **js
                         sizeof(keybuf),
                         fv->key, fv->keylen);
 
-                uint32_t len = fv->data.fv_str.value_len;
-                uint8_t printable_buf[len + 1];
-                offset = 0;
-                PrintStringsToBuffer(printable_buf, &offset,
-                        sizeof(printable_buf),
-                        fv->data.fv_str.value, fv->data.fv_str.value_len);
-
                 jb_start_object(js_flowvars);
-                jb_set_string(js_flowvars, (const char *)keybuf, (char *)printable_buf);
+                SCJbSetPrintAsciiString(js_flowvars, (const char *)keybuf, fv->data.fv_str.value,
+                        fv->data.fv_str.value_len);
                 jb_close(js_flowvars);
             } else if (fv->datatype == FLOWVAR_TYPE_INT) {
                 const char *varname = VarNameStoreLookupById(fv->idx,